//
// Presentor.java
//
package com.photoexpert;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;

class Presentor extends JComponent {

    enum MOUSE_MODES {
        DRAGGING,       // scroll the picture while dragging
        SELECTING_RECT, // selecting a rectangle area
        SELECTING_TRI,  // selecting a triangle area
        PASTING,        // pasting a picture
        DRAWING         // drawing with Drawer
    }

    static Presentor Instance() {
        if(_instance == null)
            _instance = new Presentor();
        return _instance;
    }

    MOUSE_MODES getMouseMode() { return _mouseMode; }

    void setMouseMode(MOUSE_MODES mode) {
        _logger.log(Level.INFO, "Set mouse mode to {0}", mode);
        switch(_mouseMode) {
            case SELECTING_RECT :
                MRectangleSelector.Instance().reset();
                break;
            case SELECTING_TRI :
                MTriangleSelector.Instance().reset();
                break;
            case PASTING :
                MPaster.Instance().reset();
                break;
            case DRAGGING :
                MDragger.Instance().reset();
                break;
            case DRAWING :
                MDrawer.Instance().reset();
                break;
        }
        _mouseMode = mode;
        switch(_mouseMode) {
            case SELECTING_RECT :
                MRectangleSelector.Instance().reset();
                break;
            case SELECTING_TRI :
                MTriangleSelector.Instance().reset();
                MTriangleSelector.Instance().initializeSelection();
                break;
            case PASTING :
                MPaster.Instance().reset();
                break;
            case DRAGGING :
                MDragger.Instance().reset();
                break;
            case DRAWING :
                MDrawer.Instance().reset();
                break;
        }
        App.Instance().mouseModeChanged();
    }

    void refreshImage() {
        Dimension d = getPreferredSize();
        setPreferredSize(d);
        revalidate();
        Ruler.getHRuler().setPreferredWidth(d.width);
        Ruler.getVRuler().setPreferredHeight(d.height);
        if(_implementor.hasTransparency())
            prepareBackground();
        repaint();
    }

    void setZoomRate(double rate) {
        _logger.log(Level.INFO, "Set zoom rate to {0}", rate);
        _zoomRate = rate;
        Dimension dimension = getPreferredSize();
        setPreferredSize(dimension);
        revalidate();
        Ruler.getHRuler().setPreferredWidth(dimension.width);
        Ruler.getHRuler().repaint();
        Ruler.getVRuler().setPreferredHeight(dimension.height);
        Ruler.getVRuler().repaint();
        repaint();
        MRectangleSelector.Instance().zoomRateChanged();
    }

    double getZoomRate() { return _zoomRate; }

    @Override
    public Dimension getPreferredSize()
    {
        int width = _width;
        int height = _height;
        _width = _implementor.getWidth();
        _height = _implementor.getHeight();
        if(_zoomRate == 0d) {
            _width = _implementor.getWidth();
            _height = _implementor.getHeight();
            Rectangle rect = getVisibleRect();
            double fitZoomRateW = rect.width * 1d / _width;
            double fitZoomRateH = rect.height * 1d / _height;
            if(fitZoomRateH < fitZoomRateW)
                _zoomRate = fitZoomRateH;
            else
                _zoomRate = fitZoomRateW;
        }
        _width = (int)(_width * _zoomRate);
        _height = (int)(_height * _zoomRate);
        if(_implementor.hasTransparency() && (_width > width || _height > height))
            prepareBackground();
        return new Dimension(_width, _height);
    }

    public void finishPendingOperation() {
        switch(_mouseMode) {
            case SELECTING_RECT :
                MRectangleSelector.Instance().reset();
                break;
            case SELECTING_TRI :
                MTriangleSelector.Instance().reset();
                break;
            case PASTING :
                MPaster.Instance().reset();
                break;
            case DRAGGING :
                MDragger.Instance().reset();
                break;
            case DRAWING :
                MDrawer.Instance().reset();
                break;
        }
    }

    /* --------------------------------------------------------------------- */

    private Presentor() {
        setOpaque(true);
        setBackground(_bgColor);
    }

    @Override
    public void paint(Graphics g) {
        Rectangle visibleRect = getVisibleRect();
        g.setColor(_bgColor);
        if(visibleRect.x + visibleRect.width > _width)
            g.fillRect(
                _width,
                visibleRect.y,
                visibleRect.x + visibleRect.width - _width,
                visibleRect.y + visibleRect.height);
        if(visibleRect.y + visibleRect.height > _height)
            g.fillRect(
                visibleRect.x,
                _height,
                _width - visibleRect.x,
                visibleRect.y + visibleRect.height - _height);
        BufferedImage img = _implementor.getImage();
        if(img != null) {
            if(_implementor.hasTransparency())
                g.drawImage(_background, 0, 0, null);
            g.drawImage(img, 0, 0, _width, _height, null);
            switch(_mouseMode) {
                case PASTING :
                    RectSelection s1 = MPaster.Instance().getSelection();
                    RectSelection.Area area = s1.getScreenArea();
                    BufferedImage bi = MPaster.Instance().getPastingImage();
                    g.drawImage(bi, area._x, area._y, area._width + 1, area._height + 1, null);
                    s1.drawBorder(g);
                    break;
                case SELECTING_RECT :
                    RectSelection s2 = MRectangleSelector.Instance().getSelection();
                    if(s2 != null)
                        s2.drawBorder(g);
                    break;
                case SELECTING_TRI :
                    TriangleSelection s3 = MTriangleSelector.Instance().getSelection();
                    if(s3 != null)
                        s3.drawBorder(g);
                    break;
                case DRAWING :
                    MDrawer.Instance().paint(g);
                    break;
            }
        }
    }

    private void prepareBackground() {
        _background = new BufferedImage(_width, _height, BufferedImage.TYPE_INT_RGB);
        Graphics g = _background.createGraphics();
        int x = 0;
        int col = 0;
        while(x < _width - 1) {
            int y = 0;
            int row = 0;
            while(y < _height - 1) {
                Color color = (row + col) % 2 == 0 ? Color.LIGHT_GRAY : Color.WHITE;
                g.setColor(color);
                int width = x + 10 <= _width ? 10 : _width - x;
                int height = y + 10 <= _height ? 10 : _height - y;
                g.fillRect(x, y, width, height);
                y += 10;
                row++;
            }
            x += 10;
            col++;
        }
        g.dispose();
    }

    private static Presentor    _instance = null;
    private static final Logger _logger = App._logger;
    private Implementor         _implementor = Implementor.Instance();
    // background is used when the working image has transparency
    private BufferedImage       _background = null;
    // Zoom Rate:
    // 0 = fit screen, will be calculated to again to negative
    // 1 = original size
    // n = n times of original size
    private double              _zoomRate = 1d;
    private int                 _width = 0;
    private int                 _height = 0;
    private Color               _bgColor = new Color(226, 226, 226);
    private MOUSE_MODES         _mouseMode = MOUSE_MODES.DRAGGING;
}
